package org.example.config2;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;


/**
 * @Author GongXl
 * @Description
 * @Date 2021/3/22 23:26
 * @Param
 * @return
 * https://www.jianshu.com/p/62a0a9a78530
 **/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

//    @Autowired
//    MyUserDetailsService myDetailService;

    @Autowired
    private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource;

    protected Log log = LogFactory.getLog(this.getClass());

    @Autowired
    private AuthenticationProvider authenticationProvider;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.authenticationProvider(authenticationProvider);
    }

    //定义登陆成功返回信息
    private class AjaxAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {

            //User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            log.info("用户[" + SecurityContextHolder.getContext().getAuthentication().getPrincipal() +"]登陆成功！");
            //登陆成功后移除session中验证码信息
            request.getSession().removeAttribute("codeValue");
            request.getSession().removeAttribute("codeTime");
            MyWebAuthenticationDetails myWebAuthenticationDetails = (MyWebAuthenticationDetails)authentication.getDetails();
            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            out.write("{\"status\":\"ok\",\"jwtToken\":\""+myWebAuthenticationDetails.getJwtToken()+"\",\"msg\":\"登录成功\"}");
            out.flush();
            out.close();
        }
    }

    //定义登陆失败返回信息
    private class AjaxAuthFailHandler extends SimpleUrlAuthenticationFailureHandler {
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
            //登陆失败后移除session中验证码信息
            request.getSession().removeAttribute("codeValue");
            request.getSession().removeAttribute("codeTime");

            response.setContentType("application/json;charset=utf-8");
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            PrintWriter out = response.getWriter();
            out.write("{\"status\":\"error\",\"msg\":\"请检查用户名、密码或验证码是否正确\"}");
            out.flush();
            out.close();
        }
    }

    //定义异常返回信息UnauthorizedEntryPoint定义了异常处理的逻辑，同理我们也是直接返回ajax信息而不做跳转
    public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
//            401未授权
            response.sendError(HttpStatus.UNAUTHORIZED.value(),authException.getMessage());
        }

    }

    /**
     * @Author GongXl
     * @Description 定义登出成功返回信息
     * @Date 2021/3/27 11:32
     * @Param
     * @return
     * 参考：http://www.tianshouzhi.com/api/tutorials/spring_security_4/267
     **/
    private class AjaxLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler  {
        public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            out.write("{\"status\":\"logout\",\"msg\":\"退出成功\"}");
            out.flush();
            out.close();
        }
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .exceptionHandling().authenticationEntryPoint(new UnauthorizedEntryPoint())
                .and()
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/login","/users/login_page","/captcha").permitAll()
                .anyRequest().authenticated()
                .and().formLogin().loginPage("/users/login_page")
                .successHandler(new AjaxAuthSuccessHandler())
                .failureHandler(new AjaxAuthFailHandler())
//                方便ajax或移动端调用（和controller中自定义的/login方法没有关系，是调用security中的方法）默认是login
                .loginProcessingUrl("/login")
                .authenticationDetailsSource(authenticationDetailsSource)
//                .usernameParameter("username")
//                .passwordParameter("password")
                .and()
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler(new AjaxLogoutSuccessHandler())
                .invalidateHttpSession(true);
//                .logoutUrl("/logout");
    }
    /**
     * @Author GongXl
     * @Description 一些静态资源如css、js文件通常也都不需要验证权限，我们需要将它们的访问权限也开放出来。
     * @Date 2021/3/23 15:46
     * @Param [web]
     * @return void
     **/
    @Override
    public void configure(WebSecurity web) throws Exception {
        //将项目中静态资源路径开放出来
        web.ignoring().antMatchers(
//                "/login",
//                "/logout",
                "/config/**",
                "/css/**",
                "/fonts/**",
                "/img/**",
                "/js/**",
                "/index.html"
                )
                .antMatchers("/favicon.ico")
                .antMatchers("/doc.html")
                .antMatchers("/swagger-ui.html")
                .antMatchers("/v2/**")
                .antMatchers("/webjars/**")
                .antMatchers("/swagger-resources/**");
    }
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}